#define __namevaluescpp__

#include "stdafx.h"
#include "namevalues.h"
#include <iostream>

/////////////////////////////////////////////////////////////////////////////////////////
// Name Value Structure and items                                                      //
/////////////////////////////////////////////////////////////////////////////////////////

// Name-value structure. Any record contains one or more of these
// Not ecapsulates multiple data types.
// A Union would be useful but need the string version for all types.
;

// String represetation of Types of data as immediately above.
// Just use array indexed on data type to get its string
char * DataTypeToString[7] = {
	"String", "Boolean", "Integer", "Float", "Null", "Unknown", "DateTime"
};

//Get the 1D Index to NameValues for NameValue in Records based upon Record number R
// and Name Value index I within record

//1D Array of name value pairs
NameValue * NameValues = NULL;

int RecordNo = 0;       //Counts number of records, ie when parsing is done = num of records.
int NameValueIndex = 0; //Counts number of name value pairs in each record, ie when done
int TotalNumNameValues = 0;
//is number of name value pairs in each record. (Assumed same for all records.)
int total_num_allocated = 0;
int numNameValuePairsPerRecord = 0;
 

void InitNameValues()
{
	NameValueIndex = 0;
	TotalNumNameValues = 0;
	total_num_allocated = 0;
	RecordNo = 0;
	NameValues = NULL;
	numNameValuePairsPerRecord = 0;
}

int AddNameValue(NameValue item,int recordNo, int fieldIndex)
{
	//REF:http://fydo.net/gamedev/dynamic-arrays
	if (TotalNumNameValues == total_num_allocated) // Are more refs required?
	{


		// Make the reallocation transactional 
		// by using a temporary variable first
		void *_tmp = realloc(NameValues, (total_num_allocated + NUM_NAMESVALUES_TO_ALLOCATE) * sizeof(NameValue) );

		// If the reallocation didn't work, probably out of heap,
		// inform the user and bail out
		if (!_tmp)
		{
			fprintf(stderr, "ERROR: Couldn't realloc memory!\n");
			return(-1);
		}

		// Things are looking good so far
		NameValues = (NameValue*)_tmp;

		// Array size
		total_num_allocated += NUM_NAMESVALUES_TO_ALLOCATE; //Add more
	}

	//NameValues[NameValueIndex++] = item;
	AddRecord(item, TotalNumNameValues, recordNo, fieldIndex);
	TotalNumNameValues++;
	return TotalNumNameValues;
}

extern "C" void DeallocateNameValues()
{
	// Deallocate!
	//try{
	//	free(NameValues);
	//	InitNameValues();
	//}
	//catch (...)
	//{
	//}

	//try
	//{
		free(NameValues);
		InitNameValues();
	//}
	//catch (std::exception & ex)	// ** 3 **
	//{
	//	std::cerr <<  ex.what() << std::endl;
	//}
}

void AddRecord(NameValue item, int index, int recordNo, int fieldIndex)
	{
		// Completed a name value pair to format print it.
		//DisplayANameValuePair(RecordNo,NameValueIndex);

		strcpy(NameValues[index].Name, item.Name);
		strcpy(NameValues[index].StringValue, item.StringValue);
		NameValues[index].DType = item.DType;
		switch (NameValues[index].DType)
		{
		case tString:
			break;
		case tBoolean:
			NameValues[index].BooleanValue = item.BooleanValue;
			break;
		case tInteger:
			NameValues[index].IntegerValue = item.IntegerValue;
			break;
		case tFloat:
			NameValues[index].FloatValue = item.FloatValue;
			break;
		case tNull:
			break;
		case tDate:
			NameValues[index].DateValue= item.DateValue;
			break;
		}
		DisplayANameValuePair(recordNo-1, fieldIndex);
	}


/////////////////////////////////////////////////////////////////////////////////////////////////////////
// Printing Records
/////////////////////////////////////////////////////////////////////////////////////////////////////////

// Formats and prints one name-value pair.
// Format depends upon data type
extern "C" void DisplayANameValuePair(int rec_no, int nv_no)
{
	//Indexing
	int index = REC_NO(rec_no, nv_no);
	//if (index >= MAX_NO_NAME_VALUE_PAIRS)
	//	return;
	output.print(F(" ("));
	output.print(index);
	output.print(F(","));
	output.print(rec_no);
	output.print(F(","));
	output.print(nv_no);
	output.print(F(") "));

	output.print(F(" Name:"));
	output.print(NameValues[index].Name);

	output.print(F(" Value:"));
	if (NameValues[index].DType == tString)
	{
		output.print(F("\""));
		output.print(NameValues[index].StringValue);
		output.print(F("\""));
	}
	else if (NameValues[index].DType == tBoolean)
	{
		if (NameValues[index].BooleanValue)
			output.print(F("true"));
		else
			output.print(F("false"));
	}
	else if (NameValues[index].DType == tInteger)
	{
		output.print(NameValues[index].IntegerValue);
	}
	else if (NameValues[index].DType == tFloat)
	{
		output.print_f(NameValues[index].FloatValue);
	}
	else if (NameValues[index].DType == tNull)
	{
		output.print(F("null"));
	}
	else if (NameValues[index].DType == tDate)
	{
		output.print((int)NameValues[index].DateValue);
	}
	else //tUnknown
	{
		output.print(F("Unknown"));
	}

	output.print(F(" ValueType:"));
	output.println(DataTypeToString[NameValues[index].DType]);
}


void PrintArray()
{
	output.print(F("Number of Records:"));
	output.println(RecordNo);
	output.print(F("Number of values per record:"));
	output.println(numNameValuePairsPerRecord);
	output.println();

	output.println(F("Array:"));
	for (int r = 0; r < RecordNo; r++)
	{

		//Where more than one value for a sensor display, only the most recent
		int index = REC_NO(r, ID_INDEX);
		if (NameValues[index].IntegerValue == -1)
			continue;
		output.print(F("Record:"));
		output.println(r + 1);
		for (int i = 0; i < numNameValuePairsPerRecord; i++)
		{
			DisplayANameValuePair(r, i);
		}
	}
}


/*
*  Assumes records are ordered from most recent.
*  That is handled by the AzMS GETfunction with query ?Embedded=1
*  Starting at most recent sensor record, check all subsequent records for same sensor name.
*  If same then then set the id = -1
*  Repeat for all earlier sensors
*/
extern "C" void  CheckArray()
{
	//Predicated upon received records being sorted descending wrt time, as per the GET query
	for (int r = 0; r < RecordNo; r++)
	{ 
		int rr = RecordNo -1- r;
		//ID_INDEX points to the nv field that is the record ID
		int index = REC_NO(rr, ID_INDEX);
		//Where more than one value for a sensor display only the most recent
		//ie If already "tagged" then skip check
		//Tag a record by setting its id to -1
		if (NameValues[index].IntegerValue == -1)
			continue;
		for (int r2 = r + 1; r2 < RecordNo; r2++)
		{
			int rr2 = RecordNo -1- r2;
			int index2 = REC_NO(rr2, SENSOR_INDEX);
			int index3 = REC_NO(rr, SENSOR_INDEX);
			if (strcmp(NameValues[index2].StringValue , NameValues[index3].StringValue)==0)
			{
				NameValues[index].IntegerValue = -1;
				break;
			}
		}
	}
}


/////////////////////////////////////////////////////////////////////////////////////////////////////////


#undef __namevaluescpp__